// ptrace-test.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/user.h>

#define WORD_SIZE (sizeof(long))

union word_w {
	unsigned long long int val;
	char chars[WORD_SIZE];
}; 
typedef union word_w WORD; // WORD 类型用于获取从被跟踪进程中取出的1个字大小的内存数据

void getData(pid_t child,unsigned long begin, unsigned long len, char * str)
{
	unsigned long i, j;
    WORD w;

	char * pos = str;
	/* 一次拷贝一个字的数据 */
	for (i = 0; i < len / WORD_SIZE; i++) {
			w.val = ptrace(PTRACE_PEEKDATA, child, begin + i * WORD_SIZE, NULL);
			memcpy(pos, w.chars, WORD_SIZE);
			pos += WORD_SIZE;
		}
		/* 拷贝数据结尾不足一个字的数据 */
		j = len % WORD_SIZE;
		if (j) {
			w.val = ptrace(PTRACE_PEEKDATA, child, begin + i * WORD_SIZE, NULL);
			memcpy(pos, w.chars, j);
		}
		str[len] = '\0';
	}


int main()
{
	pid_t child; // 子进程编号
	struct user_regs_struct regs; 
	child = fork(); // 创建子进程
	int status;
	int insyscall = 0;
	unsigned long i, len, j;
	unsigned long begin;
	char * str = NULL, * pos = NULL;
	WORD w;

	if (child == 0) {
		ptrace(PTRACE_TRACEME, 0, NULL, NULL); // 接受跟踪请求
		execl("/bin/ls", "ls", NULL); // 执行 ls 命令
	}
	else {
		while(1) {
			// 等待子进程进行系统调用或结束
			wait(&status); 
			
			// 如果进程处于结束状态则跳出循环
			if(WIFEXITED(status)) { 
				break;
			}
			// 查询此时的寄存器的值
			ptrace(PTRACE_GETREGS, child, NULL, &regs); 
			if (insyscall == 0) { // 如果当前是进入系统调用
				insyscall = 1;
				// 打印寄存器的值
				printf("syscall: no.%llu params %llu, %llu, %llu\n", 
					regs.orig_rax, regs.rdi, regs.rsi, regs.rdx); 
				// 如果是 write 系统调用
				if (regs.orig_rax == SYS_write) {
					printf("------>write syscall\n");
					len = regs.rdx;
					begin = regs.rsi;
					str = (char *)malloc(sizeof(char) * (len + 1));
					/* 拷贝出被跟踪进程输出字符串的内存数据 */
					getData(child,begin, len, str);
					/* 将字符串打印出来 */
					printf("\n------>write : \n%s \n\n", str);
				}
			}
			else {
				// 输出系统调用的返回值
				printf("syscall: returns %lld\n", regs.rax); 
				insyscall = 0;
			}
			
			ptrace(PTRACE_SYSCALL, child, NULL, NULL);
		}
		
	}

	return 0;
}